# 指定文件编码为UTF-8
# coding: utf-8

"""
overfit_weight_decay.py 功能说明：
1. 演示权重衰减(Weight Decay/L2正则化)对过拟合的抑制效果
2. 使用MNIST数据集，通过减少训练数据量(300个样本)人为制造过拟合
3. 比较使用权重衰减和不使用权重衰减时的训练集/测试集准确率差异
4. 可视化训练过程中的准确率变化曲线

关键点：
- 使用6层深度网络(每层100个神经元)增强过拟合倾向
- 权重衰减通过在损失函数中添加L2正则项来抑制大权重值
- 训练201个epoch观察长期训练效果
- 使用SGD优化器，学习率0.01
- 绘制训练/测试准确率曲线对比图
"""

# 导入操作系统模块
import os
# 导入系统模块
import sys
# 添加父目录到系统路径，以便导入父目录中的模块
sys.path.append(os.pardir)

# 导入NumPy数值计算库
import numpy as np
# 导入matplotlib绘图库
import matplotlib.pyplot as plt
# 从dataset.mnist导入MNIST数据加载函数
from dataset.mnist import load_mnist
# 从common.multi_layer_net导入多层网络类
from common.multi_layer_net import MultiLayerNet
# 从common.optimizer导入SGD优化器
from common.optimizer import SGD

# 加载MNIST数据集(自动归一化)
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 人为减少训练数据量到300个样本，以制造过拟合
x_train = x_train[:300]
t_train = t_train[:300]

# 权重衰减系数设置 ================# ... 省略其他代码 ...


# ==== 修改后代码 ====
#weight_decay_lambda = 0 # 不使用权重衰减的情况
weight_decay_lambda = 0.1  # 使用权重衰减的情况
# ===============================# ... 省略其他代码 ...


# ==== 修改后代码 ====

# 初始化6层神经网络(每层100个神经元)
network = MultiLayerNet(input_size=784,
                       hidden_size_list=[100, 100, 100, 100, 100, 100],
                       output_size=10,
                       weight_decay_lambda=weight_decay_lambda)  # 设置权重衰减系数

# 使用SGD优化器，学习率0.01
optimizer = SGD(lr=0.01)

# 训练参数设置
max_epochs = 201  # 最大训练轮数
train_size = x_train.shape[0]  # 训练数据大小
batch_size = 100  # 批量大小

# 初始化记录列表
train_loss_list = []  # 训练损失记录
train_acc_list = []  # 训练准确率记录
test_acc_list = []  # 测试准确率记录

# 计算每个epoch包含的迭代次数
iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0  # epoch计数器

# 训练循环
for i in range(1000000000):
    # 随机选择批量数据
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]  # 当前批次输入数据
    t_batch = t_train[batch_mask]  # 当前批次标签数据

    # 计算梯度(自动包含权重衰减项)
    grads = network.gradient(x_batch, t_batch)
    # 更新参数
    optimizer.update(network.params, grads)

    # 每个epoch结束时评估模型
    if i % iter_per_epoch == 0:
        # 计算训练集和测试集准确率
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        # 记录准确率
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)

        # 打印当前训练进度
        print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))

        epoch_cnt += 1
        # 达到最大epoch数时停止训练
        if epoch_cnt >= max_epochs:
            break

# 结果可视化 ===# ... 省略其他代码 ...


# ==== 修改后代码 ====
# 设置绘图标记样式
markers = {'train': 'o', 'test': 's'}
# 创建x轴数据(epoch数)
x = np.arange(max_epochs)
# 绘制训练集准确率曲线(圆圈标记)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
# 绘制测试集准确率曲线(方块标记)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
# 设置坐标轴标签
plt.xlabel("epochs")
plt.ylabel("accuracy")
# 设置y轴范围
plt.ylim(0, 1.0)
# 显示图例
plt.legend(loc='lower right')
# 显示图形
plt.show()

"""
预期结果分析：
1. 不使用权重衰减时：
   - 训练准确率会快速上升接近100%
   - 测试准确率会明显低于训练准确率(过拟合)
2. 使用权重衰减时：
   - 训练准确率上升较慢
   - 测试准确率与训练准确率差距缩小
   - 最终测试性能通常更好

注意事项：
1. 权重衰减系数需要适当调整(通常0.01-0.1)
2. 网络越深/越宽，权重衰减效果通常越明显
3. 可以尝试与其他正则化方法(Dropout等)结合使用
4. 权重衰减会增加模型偏差，减少方差
"""
